/*
    Copyright Rysiek Labus SQ9MDD
 APRS Platform na bazie arduino i QAPRS.
 
 APRS Platform is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 
 APRS Platform is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with ArduinoQAPRS; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 
 APRS Platform jest wolnym oprogramowaniem; możesz go rozprowadzać dalej
 i/lub modyfikować na warunkach Powszechnej Licencji Publicznej GNU,
 wydanej przez Fundację Wolnego Oprogramowania - według wersji 2 tej
 Licencji lub (według twojego wyboru) którejś z późniejszych wersji.
 
 Niniejszy program rozpowszechniany jest z nadzieją, iż będzie on
 użyteczny - jednak BEZ JAKIEJKOLWIEK GWARANCJI, nawet domyślnej
 gwarancji PRZYDATNOŚCI HANDLOWEJ albo PRZYDATNOŚCI DO OKREŚLONYCH
 ZASTOSOWAŃ. W celu uzyskania bliższych informacji sięgnij do
 Powszechnej Licencji Publicznej GNU.
 
 Z pewnością wraz z niniejszym programem otrzymałeś też egzemplarz
 Powszechnej Licencji Publicznej GNU (GNU General Public License);
 jeśli nie - napisz do Free Software Foundation, Inc., 59 Temple
 Place, Fifth Floor, Boston, MA  02110-1301  USA
 
 Changelog
 
 2013.07.16 Stabilna wersja 1.0 obsługa wysyłania bikonu, zapis konfiguracji 
 do eepromu. 
 
 2013.07.19 Dodanie obsługi carrier detect PIN 2, 
 dodanie wymuszania wysłania beaconu za pomocą pinu 4
 
 */
#include <EEPROM.h>
#include <Arduino.h>
#include "ArduinoQAPRS.h"
#define version 1.0;

//zmienne, nie dotykac
char from_addr[] = "NOLIC ";              //znak
char * relays = "                    ";   //20 znaków
char * comment_arr = "                                                             ";//bufor na ramkę 60 znaków 19 na koordynaty symboj i tablice.
char * packet_buffer  = "                                                                    \n ";
int volt_indic = 0;                       //czy pokazac napięcie?
int ssid = 0;                             //domyślne SSID
int beacon_interwal = 1;                  //domyślny czas wysyłania beaconu w minutach
int napiecie_zasilania_pin = A3;          //pin pomiaru napięcia
String content = "";
unsigned long time_to_beacon = millis() + (beacon_interwal*60000); 
int licz = 0;
int sendBeaconNow = 4;
int minuta = 0;
unsigned long time_to_check = 0;
//ustawiamy zmienną z czasem
unsigned long time = millis();

//uwaga carier detect biblioteki QAPRS pracuje zwsze ze stanem wysokim
//to znaczy stan wysoki blokuje nadawanie.
//jeśli pojawi się obsługa +5V lub 0V jako opcja będzie można usunąć 
//obsługę własnego carrier detect
int carrierDetectPin = 2;
int carrierDetectOn = 0;
int carrierDetectDelay = 1500; //do eepromu zapiszemy wartość podzieloną przez 100

//pokaż bieżącą konfigurację
void show_c(){
  Serial.println("Current config:"); 
  Serial.print("mca=     ");
  Serial.println(from_addr);
  Serial.print("mti=     ");
  Serial.println(beacon_interwal);
  Serial.print("mpa=     ");
  Serial.println(relays);
  Serial.print("mra=     ");
  Serial.println(comment_arr);
  Serial.print("mpv=     "); 
  Serial.println(volt_indic); 
  Serial.print("mcd=     "); 
  Serial.println(carrierDetectOn);   
  Serial.print("mdd=     "); 
  Serial.println(carrierDetectDelay);   
}

//zapis zmiennych do eepromu
void save_to_eeprom(){
  EEPROM.write(0,1);                     //zaznaczamy ze zapisana jest pamiec
  EEPROM.write(1,volt_indic);            //zaznaczamy wyswietlanie napiecia
  EEPROM.write(2,ssid);                  //zapisujemy ssid
  EEPROM.write(3,beacon_interwal);       //zapisujemy co ile wysylamy beacon  
  //zapis callsign do eeprom 6 znaków
  for(int a = 4; a <= 9; a++){
    EEPROM.write((a),from_addr[a-4]);
  }
  //zapis path 20 znaków
  for(int b = 10; b <= 29; b++){
    EEPROM.write((b),relays[b-10]);
  }
  //zapis komentarz 60 znaków
  for(int c = 30; c <= 90; c++){
    EEPROM.write((c),comment_arr[c-30]);
  }
  EEPROM.write(91,carrierDetectOn);
  EEPROM.write(92,(carrierDetectDelay/100));
}

//odczytanie zmiennych z eepromu i  wczytanie do zmiennch lokalnych albo ustawienie domyślne
void read_from_eeprom_or_make_default(){
  int skonfigurowany = EEPROM.read(0);
  if(skonfigurowany == 1){
    volt_indic = EEPROM.read(1);
    ssid = EEPROM.read(2);
    beacon_interwal = EEPROM.read(3);
    //odczyt callsign z eeprom
    for(int a = 4; a <= 9; a++){
      from_addr[a-4] = EEPROM.read(a);
    }
    //odczyt sciezki
    for(int b = 10; b <= 29; b++){
      relays[b-10] = EEPROM.read(b);     
    }    
    //odczyt kometarz koordynaty symbol i tabela z eeprom'u
    for(int c = 30; c <= 90; c++){
      comment_arr[c-30] = EEPROM.read(c);     
    } 
    carrierDetectOn = EEPROM.read(91);   
    carrierDetectDelay = EEPROM.read(92)*100;   
  }
  else{
    //domyślne wartości
    relays = "WIDE1 0";
    comment_arr = ">EMPTY EEPROM";
  }
}

//czyszczenie zawartości eeprom poprzez nadpisanie wartością 0xFF całej pamięci 
void clear_eeprom_memory(){
  for(int a = 0; a < 1024; a++){
    EEPROM.write(a,0xFF);
    delay(10);
  }
}

//reset jakby trzeba było sprawdzić konfigurację, no i po czyszczeniu eeproma sie przydaje
void softReset(){
  asm volatile ("  jmp 0");
}

//przygotowujemy dane i wysyłamy ramkę
void prepare_and_send_beacon_f(){
  int napiecie_zasilania = map(analogRead(napiecie_zasilania_pin),0,1023,0,159);
  int v_prefix = napiecie_zasilania/10;
  int v_sufix = napiecie_zasilania%10;
  //komentarz z eepromu
  for(int c = 30; c <= 90; c++){
    comment_arr[c-30] = EEPROM.read(c);     
  } 
  if(volt_indic==1){
    sprintf(packet_buffer, "%s %04u %02u.%01uV", comment_arr,licz,v_prefix,v_sufix);
  }
  if(volt_indic==0){
    sprintf(packet_buffer, "%s %04u", comment_arr,licz);
  }  
  QAPRS.send(from_addr, ssid, "APZMDD", '0', relays, packet_buffer);
  licz++; 
}

//mala pomoc jesli ktoś nie ma aplikacji
void print_help(){
  Serial.println("APRS Platform based on Arduino and QAPRS library.\ncommands:"); 
  Serial.println("mca=     callsign");
  Serial.println("mti=     beacon timing (m)");
  Serial.println("mpa=     path");
  Serial.println("mra=     beacon text including symbol, table and coordinates");
  Serial.println("mpv=     voltage in beacon");
  Serial.println("mcd=     carrier detect voltage +5V or 0V");
  Serial.println("send     sending beacon");
  Serial.println("cls      clearing eeprom memory");
  Serial.println("mem      writing current config to memory");
  Serial.println("reset    reload device");
  Serial.println("showc    show current config");
  Serial.println("showe    show eeprom memory");
  Serial.println("help,?   printing this manual");
}

//obowiązkowo setupik na starcie procesora
void setup(){
  pinMode(napiecie_zasilania_pin,INPUT);
  pinMode(carrierDetectPin,INPUT);
  pinMode(sendBeaconNow,INPUT_PULLUP);
  QAPRS.init(0,3);
  Serial.begin(9600);
  read_from_eeprom_or_make_default();
}

//petla główna
void loop(){
time = millis();
  //słuchamy eresa
  while(Serial.available()) {
    char character = Serial.read();
    content.concat(character);
    delay (10);
  }
  if (content != "") {
    content.trim();//wywalamy CR i LF z wchodzących stringów
    time_to_beacon = time + (beacon_interwal*60000);
    //zmiana znaku
    if(content.substring(0,4) == "mca="){
      content.substring(4).toCharArray(from_addr,7);
      int by_position = content.indexOf('-')+1;     
      ssid = content.substring(by_position).toInt();
    }
    if(content.substring(0,4) == "mti="){
      beacon_interwal = content.substring(4).toInt();
      //Serial.print(a);
    } 
    if(content.substring(0,4) == "mpa="){
      content = content.substring(4);      
      int dlugosc = content.length()+1;
      content.toCharArray(relays,dlugosc);      
    }
    if(content.substring(0,4) == "mpv="){
      if(content.substring(4) == "1"){
        volt_indic = 1;
      }
      if(content.substring(4) == "0"){
        volt_indic = 0;
      }
    }
    if(content.substring(0,4) == "mcd="){
      if(content.substring(4) == "1"){
        carrierDetectOn = 1;
      }
      if(content.substring(4) == "0"){
        carrierDetectOn = 0;
      } 
    }
    if(content.substring(0,4) == "mdd="){
      carrierDetectDelay = content.substring(4).toInt();
      carrierDetectDelay = constrain(carrierDetectDelay,500,5000);
    }
    if(content.substring(0,4) == "mra="){
      content = content.substring(4);
      int dlugosc = content.length()+1;
      content.toCharArray(comment_arr,dlugosc);      
    }   
    if(content.substring(0,4) == "send"){
      time_to_beacon = time;
    }
    if(content.substring(0,3) == "cls"){
      clear_eeprom_memory();  
    }  
    if(content.substring(0,3) == "mem"){
      save_to_eeprom();  
    } 
    if(content.substring(0,5) == "reset"){
      softReset();  
    } 
    if(content.substring(0,5) == "showc"){
      show_c();  
    }     
    if(content.substring(0,4) == "help"){
      print_help();
    }
    if(content.substring(0,1) == "?"){
      print_help();
    } 
    content = ""; 
  }  
  //wymuszenie wysłania beaconu
  int wymuszenie_state = digitalRead(sendBeaconNow);
  if(wymuszenie_state == LOW){
    time_to_beacon = time;
  }
  //wysyłamy ramkę
  if (time >= time_to_beacon && beacon_interwal!= 1){
    int carrierDetectState = digitalRead(carrierDetectPin);
    //jesli carrier detect ma stan taki jak wybrany w configu to przekładamy czas nadania ramki o 1500ms
    if(carrierDetectState == carrierDetectOn){
      time_to_beacon = time + carrierDetectDelay;
    }
    if (time >= time_to_beacon){
      prepare_and_send_beacon_f();
      time_to_beacon = time + (beacon_interwal*60000);
    }
  }

  //Kiedy wartość 'time' przekroczy maksimum (4294967295 czyli około 49dni) i zacznie liczyć od nowa, 
  //to funkcje nie będą wywoływane bo czas będzie zawsze mniejszy niż wyliczony z max time + czas wykonania.
  //dlatego resetujemy czas w przypadku gdy licznik time się przekręca.
  if (time==0){
    time_to_beacon = beacon_interwal*60000;
  }
}
